from game.gameobjects.matrix44 import *
from game.gameobjects.vector3 import *
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
from math import radians

class Camera:
    """
    This is the Camera class, which controls the viewport. The default
    behaviour of the camera is a flying camera
    """
    def __init__(self):
        
        self.current_mode = None
        self.modes = {
            'free': FreeCamera(),
            'fps': FpsCamera(),
            'nav': NavCamera()
        }
        self.set_mode('free')


    def set_mode(self, mode, **args):

        if self.current_mode is not None:
            self.current_mode.pause(**args)

        self.current_mode = self.modes[mode]
        self.current_mode.resume(**args)


    def set_camera(self):
        self.current_mode.set_camera()

    def event(self, event, ms):
        self.current_mode.event(event, ms)

    def update(self, ms):
        self.current_mode.update(ms)

class FreeCamera:

    def __init__(self):

        self.matrix = Matrix44()
        
        self.rotation_speed = radians(90.0)
        self.movement_speed = 5.0
        self.mouse_sensitivity = 300.0

        self.rotation_vector = Vector3()
        self.movement_vector = Vector3()

        self.rotation_current = Vector3()
        self.movement_current = Vector3()

        self.rotation_min = Vector3(radians(-85.0), 0, 0)
        self.rotation_max = Vector3(radians(85.0), 0, 0)

        self.key_def = {
            K_LEFT: (self.rotation_vector, 1, 1.0),
            K_RIGHT: (self.rotation_vector, 1, -1.0),
            K_UP: (self.rotation_vector, 0, 1.0),
            K_DOWN: (self.rotation_vector, 0, -1.0),
            K_q: (self.rotation_vector, 2, 1.0),
            K_e: (self.rotation_vector, 2, -1.0),
            K_w: (self.movement_vector, 2, -1.0),
            K_s: (self.movement_vector, 2, 1.0),
            K_a: (self.movement_vector, 0, -1.0),
            K_d: (self.movement_vector, 0, 1.0),
        }

    def resume(self, **args):
        pygame.mouse.set_visible(False)
        pygame.event.set_grab(True)
        pygame.mouse.get_rel()

    def pause(self, **args):
        pass

    def set_camera(self):
        """
        This method will load the camera's matrix to the viewport of openGL
        """
        glLoadMatrixd(self.matrix.get_inverse().to_opengl())


    def event(self, event, ms):
        ms_sec = ms / 1000.
        if event.type == MOUSEMOTION:
            mouse_rel = pygame.mouse.get_rel()

            self.rotation_current.y += float(-mouse_rel[0]) / self.mouse_sensitivity

            self.rotation_current.x += float(-mouse_rel[1]) / self.mouse_sensitivity
            self.rotation_current.x = max(min(self.rotation_current.x, self.rotation_max.x), self.rotation_min.x)


        if event.type == KEYDOWN:
            if event.key in self.key_def:
                a, k, v = self.key_def[event.key]
                a[k] += v
        if event.type == KEYUP:
            if event.key in self.key_def:
                a, k, v = self.key_def[event.key]
                a[k] -= v

    def update(self, ms):
        ms_sec = ms / 1000.

        # Calculate rotation matrix and multiply by camera matrix
        if self.rotation_vector != Vector3():
            self.handle_rotation(ms_sec)

        # Calculate movement and add it to camera matrix translate
        if self.movement_vector != Vector3():
            self.handle_movement(ms_sec)


        self.matrix = Matrix44()
        self.matrix.translate = self.movement_current
        #print self.movement_current
        self.matrix *= Matrix44.y_rotation(self.rotation_current.y)
        self.matrix *= Matrix44.x_rotation(self.rotation_current.x)


    def handle_rotation(self, ms):
        rotation = self.rotation_vector * self.rotation_speed * ms
        self.rotation_current += rotation

    def handle_movement(self, ms):
        heading = Vector3(self.matrix.forward)
        strafe = Vector3(self.matrix.right)
        movement = (heading * self.movement_vector.z * self.movement_speed) + \
            (strafe * self.movement_vector.x * self.movement_speed)
        self.movement_current += (movement * ms)
        
    def handle_zoom(self, ms):
        pass

class FpsCamera(FreeCamera):
    def __init__(self):
        FreeCamera.__init__(self)

        del self.key_def[K_q]
        del self.key_def[K_e]
        self.key_def[K_SPACE] = (self.movement_vector, 1, 1.0)
        self.key_def[K_z] = (self.movement_vector, 1, -1.0)

        self.up = Vector3(0,1,0)
        self.attach = None

    def resume(self, **args):
        pygame.mouse.set_visible(False)
        pygame.event.set_grab(True)
        pygame.mouse.get_rel()
        if 'attach' in args:
            self.attach = args['attach']

    def handle_movement(self, ms):
        heading = Vector3(self.matrix.forward[0], 0, self.matrix.forward[2]).normalise()
        strafe = Vector3(self.matrix.right)
        movement = (strafe * self.movement_vector.x * self.movement_speed) + \
            (self.up * self.movement_vector.y * self.movement_speed) + \
            (heading * self.movement_vector.z * self.movement_speed)
        self.movement_current += (movement * ms)

class NavCamera(FreeCamera):
    def __init__(self):
        FreeCamera.__init__(self)
        self.key_def = {}

    def resume(self, **args):
        pygame.mouse.set_visible(True)
        pygame.event.set_grab(False)

    def update(self, ms):
        pass